use {
    super::error::ClientError,
    reqwest::{Client as HttpClient, Method, RequestBuilder, Response},
    serde::{de::DeserializeOwned, Serialize, Serializer},
    serde_json,
    tracing::debug,
    url::Url,
};

#[derive(Debug, Default)]
pub struct EmptyBody {}
impl serde::Serialize for EmptyBody {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str("")
    }
}

#[derive(Debug)]
pub struct Client {
    base_url: Url,
    http_client: HttpClient,
}

pub enum Address {
    Base58(String),
    Hex(String),
}

async fn decode_response<T>(res: Response) -> anyhow::Result<T>
where
    T: DeserializeOwned,
{
    let data = res.text().await?;
    //dbg!(&data);
    let s: T =
        serde_json::from_str(&data).map_err(|orig_err| match serde_json::from_str(&data) {
            Err(_) => {
                debug!("err resp: {}", data);
                // dbg!(&data);
                orig_err.into()
            }
            Ok(r) => ClientError::ServerError(r),
        })?;

    Ok(s)
}

pub fn build_client(env: &str) -> Client {
    let client_url_str = commons::get_client_url("TRON", env);
    Client {
        base_url: Url::parse(&client_url_str).expect("could not parse base_url"),
        http_client: HttpClient::new(),
    }
}

// https://github.com/oikos-cash/tron-api-client
impl Client {
    async fn pre_req(&self, method: Method, url: Url) -> anyhow::Result<RequestBuilder> {
        let req = self
            .http_client
            .request(method, url)
            .header("Content-Type", "application/json");
        Ok(req)
    }

    fn get_url(&self, path: &str) -> Url {
        self.base_url.join(path).expect("could not parse url")
    }

    async fn req<T, U>(&self, path: &str, method: Method, body: U) -> anyhow::Result<T>
    where
        T: DeserializeOwned,
        U: Serialize,
    {
        let res = match method {
            Method::GET => {
                self.pre_req(method, self.get_url(path))
                    .await?
                    .send()
                    .await?
            }
            Method::POST => {
                self.pre_req(method, self.get_url(path))
                    .await?
                    .json(&body)
                    .send()
                    .await?
            }
            _ => unimplemented!(),
        };
        decode_response::<T>(res).await
    }

    pub async fn post<T, U>(&self, path: &str, param: U) -> anyhow::Result<T>
    where
        T: DeserializeOwned,
        U: Serialize,
    {
        self.req(path, Method::POST, param).await
    }

    pub async fn get<T>(&self, path: &str) -> anyhow::Result<T>
    where
        T: DeserializeOwned,
    {
        self.req(path, Method::GET, EmptyBody::default()).await
    }
}
